package service

import (
	"context"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"monitoring-api/models"
	"monitoring-api/serializer"
)

type GetGraphService struct {
	EndTime int64 `json:"end_time" form:"end_time"` // 今日的结束时间戳
	WebID   int64 `json:"web_id" form:"web_id"`     // 网站id
}

func (service *GetGraphService) Query() serializer.Response {
	//获取近七天异常数
	err, errsRes := service.getErrs()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}
	//获取近七天网页性能平均值
	err, PerRes := service.getPer()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}
	//获取近七天用户访问量
	err, ViewRes := service.getViews()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}
	//获取近七天用户停留时间
	err, DurationRes := service.getDuration()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}

	//获取近七天请求成功/失败数目
	err, RespRes := service.getResp()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}
	//获取近七天平均请求响应时间
	err, ResTimeRes := service.getResTime()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}
	//过去30天用户的浏览器类型
	err, BrowserRes := service.getBrowser()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}
	//过去30天用户的地区分布
	err, AreaRes := service.getArea()
	if err != nil {
		return serializer.Response{
			Code:    500,
			Message: "服务器错误",
			Error:   err.Error(),
		}
	}

	return serializer.Response{
		Code:    0,
		Message: "获取图表数据成功",
		Data: serializer.GraphDataResponse{
			Per:     PerRes,
			Browser: BrowserRes,
			Area:    AreaRes,
			Http1:   RespRes,
			Http2:   ResTimeRes,
			User1:   ViewRes,
			User2:   DurationRes,
			Err:     errsRes,
		},
	}
}

func (service *GetGraphService) getErrs() (error, [][]int64) {
	//初始化结果数组
	res := make([][]int64, 7)
	var i int64
	for i = 0; i < 7; i++ {
		res[i] = make([]int64, 4)
	}
	var interval int64
	interval = 24 * 60 * 60 * 1000
	statTime := service.EndTime - 7*24*60*60*1000
	endTime := statTime + interval
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "$type"},
			{"sum_errs", bson.D{
				{"$sum", 1},
			}},
		},
		}}
	for i = 0; i < 7; i++ {
		match := bson.D{
			{"$match", bson.D{{"webid", service.WebID},
				{"time", bson.D{{"$gt", statTime}}},
				{"time", bson.D{{"$lte", endTime}}},
			}},
		}
		// pass the stage into a pipeline
		// pass the pipeline as the second paramter in the Aggregate() method
		cursor, err := models.Mongo.Collection("errors").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
		if err != nil {
			return err, [][]int64{}
		}
		// display the results
		var results []bson.M
		if err = cursor.All(context.TODO(), &results); err != nil {
			if err != nil {
				return err, [][]int64{}
			}
		}
		for _, result := range results {
			Type := result["_id"].(int64)
			res[i][Type] = int64(result["sum_errs"].(int32))
		}
		statTime = endTime
		endTime = statTime + interval
	}
	return nil, res
}

func (service *GetGraphService) getPer() (error, [][]int64) {
	//初始化结果数组
	res := make([][]int64, 7)
	var i int64
	for i = 0; i < 7; i++ {
		res[i] = make([]int64, 6)
	}
	var interval int64
	interval = 24 * 60 * 60 * 1000
	statTime := service.EndTime - 7*24*60*60*1000
	endTime := statTime + interval
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "null"},
			{"dcl_avg", bson.D{
				{"$avg", "$dcl"},
			}},
			{"fcp_avg", bson.D{
				{"$avg", "$fcp"},
			}},
			{"dns_avg", bson.D{
				{"$avg", "$dns"},
			}},
			{"fp_avg", bson.D{
				{"$avg", "$fp"},
			}},
			{"l_avg", bson.D{
				{"$avg", "$l"},
			}},
			{"lcp_avg", bson.D{
				{"$avg", "$lcp"},
			}},
		},
		}}
	for i = 0; i < 7; i++ {
		match := bson.D{
			{"$match", bson.D{{"webid", service.WebID},
				{"time", bson.D{{"$gt", statTime}}},
				{"time", bson.D{{"$lte", endTime}}},
			}},
		}
		// pass the stage into a pipeline
		// pass the pipeline as the second paramter in the Aggregate() method
		cursor, err := models.Mongo.Collection("performances").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
		if err != nil {
			return err, [][]int64{}
		}
		// display the results
		var results []bson.M
		if err = cursor.All(context.TODO(), &results); err != nil {
			if err != nil {
				return err, [][]int64{}
			}
		}
		for _, result := range results {
			res[i][0] = int64(result["dns_avg"].(float64))
			res[i][1] = int64(result["fp_avg"].(float64))
			res[i][2] = int64(result["fcp_avg"].(float64))
			res[i][3] = int64(result["lcp_avg"].(float64))
			res[i][4] = int64(result["dcl_avg"].(float64))
			res[i][5] = int64(result["l_avg"].(float64))
		}
		statTime = endTime
		endTime = statTime + interval
	}
	return nil, res
}

func (service *GetGraphService) getViews() (error, [][]int64) {
	//初始化结果数组
	res := make([][]int64, 7)
	var i int64
	for i = 0; i < 7; i++ {
		res[i] = make([]int64, 2)
	}
	var interval int64
	interval = 24 * 60 * 60 * 1000
	statTime := service.EndTime - 7*24*60*60*1000
	endTime := statTime + interval
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "$user"},
			{"count", bson.D{
				{"$sum", 1},
			}},
		},
		}}
	for i = 0; i < 7; i++ {
		match := bson.D{
			{"$match", bson.D{{"webid", service.WebID},
				{"time", bson.D{{"$gt", statTime}}},
				{"time", bson.D{{"$lte", endTime}}},
			}},
		}
		// pass the stage into a pipeline
		// pass the pipeline as the second paramter in the Aggregate() method
		cursor, err := models.Mongo.Collection("actions").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
		if err != nil {
			return err, [][]int64{}
		}
		// display the results
		var results []bson.M
		if err = cursor.All(context.TODO(), &results); err != nil {
			if err != nil {
				return err, [][]int64{}
			}
		}
		for _, result := range results {
			Type := result["_id"].(int64)
			if Type == 0 {
				res[i][0] += int64(result["count"].(int32))
				res[i][1] += int64(result["count"].(int32))
			} else if Type == 1 {
				res[i][0] += int64(result["count"].(int32))
				res[i][1] += int64(result["count"].(int32))
			} else if Type == 2 {
				res[i][0] += int64(result["count"].(int32))
			}
		}
		statTime = endTime
		endTime = statTime + interval
	}
	return nil, res
}

func (service *GetGraphService) getDuration() (error, [][]int64) {
	//初始化结果数组
	res := make([][]int64, 7)
	var i int64
	for i = 0; i < 7; i++ {
		res[i] = make([]int64, 1)
	}
	var interval int64
	interval = 24 * 60 * 60 * 1000
	statTime := service.EndTime - 7*24*60*60*1000
	endTime := statTime + interval
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "null"},
			{"duration_avg", bson.D{
				{"$avg", "$duration"},
			}},
		},
		}}
	for i = 0; i < 7; i++ {
		match := bson.D{
			{"$match", bson.D{{"webid", service.WebID},
				{"time", bson.D{{"$gt", statTime}}},
				{"time", bson.D{{"$lte", endTime}}},
			}},
		}
		// pass the stage into a pipeline
		// pass the pipeline as the second paramter in the Aggregate() method
		cursor, err := models.Mongo.Collection("actions").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
		if err != nil {
			return err, [][]int64{}
		}
		// display the results
		var results []bson.M
		if err = cursor.All(context.TODO(), &results); err != nil {
			if err != nil {
				return err, [][]int64{}
			}
		}
		for _, result := range results {
			res[i][0] = int64(result["duration_avg"].(float64))
		}
		statTime = endTime
		endTime = statTime + interval
	}
	return nil, res
}

func (service *GetGraphService) getResp() (error, [][]int64) {
	//初始化结果数组
	res := make([][]int64, 7)
	var i int64
	for i = 0; i < 7; i++ {
		res[i] = make([]int64, 2)
	}
	var interval int64
	interval = 24 * 60 * 60 * 1000
	statTime := service.EndTime - 7*24*60*60*1000
	endTime := statTime + interval
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "$success"},
			{"count", bson.D{
				{"$sum", 1},
			}},
		},
		}}
	for i = 0; i < 7; i++ {
		match := bson.D{
			{"$match", bson.D{{"webid", service.WebID},
				{"time", bson.D{{"$gt", statTime}}},
				{"time", bson.D{{"$lte", endTime}}},
			}},
		}
		// pass the stage into a pipeline
		// pass the pipeline as the second paramter in the Aggregate() method
		cursor, err := models.Mongo.Collection("requests").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
		if err != nil {
			return err, [][]int64{}
		}
		// display the results
		var results []bson.M
		if err = cursor.All(context.TODO(), &results); err != nil {
			if err != nil {
				return err, [][]int64{}
			}
		}
		for _, result := range results {
			Type := result["_id"].(bool)
			if Type == true {
				res[i][0] += int64(result["count"].(int32))
			} else if Type == false {
				res[i][1] += int64(result["count"].(int32))
			}
		}
		statTime = endTime
		endTime = statTime + interval
	}
	return nil, res
}

func (service *GetGraphService) getResTime() (error, [][]int64) {
	//初始化结果数组
	res := make([][]int64, 7)
	var i int64
	for i = 0; i < 7; i++ {
		res[i] = make([]int64, 1)
	}
	var interval int64
	interval = 24 * 60 * 60 * 1000
	statTime := service.EndTime - 7*24*60*60*1000
	endTime := statTime + interval
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "null"},
			{"resTime_avg", bson.D{
				{"$avg", "$restime"},
			}},
		},
		}}
	for i = 0; i < 7; i++ {
		match := bson.D{
			{"$match", bson.D{{"webid", service.WebID},
				{"time", bson.D{{"$gt", statTime}}},
				{"time", bson.D{{"$lte", endTime}}},
			}},
		}
		// pass the stage into a pipeline
		// pass the pipeline as the second paramter in the Aggregate() method
		cursor, err := models.Mongo.Collection("requests").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
		if err != nil {
			return err, [][]int64{}
		}
		// display the results
		var results []bson.M
		if err = cursor.All(context.TODO(), &results); err != nil {
			if err != nil {
				return err, [][]int64{}
			}
		}
		for _, result := range results {
			res[i][0] = int64(result["resTime_avg"].(float64))
		}
		statTime = endTime
		endTime = statTime + interval
	}
	return nil, res
}

func (service *GetGraphService) getArea() (error, []int64) {
	//初始化结果数组
	res := make([]int64, 35)
	startTime := service.EndTime - 30*24*60*60*1000
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "$area"},
			{"count", bson.D{
				{"$sum", 1},
			}},
		},
		}}
	match := bson.D{
		{"$match", bson.D{{"webid", service.WebID},
			{"time", bson.D{{"$gt", startTime}}},
			{"time", bson.D{{"$lte", service.EndTime}}},
		}},
	}
	// pass the stage into a pipeline
	// pass the pipeline as the second paramter in the Aggregate() method
	cursor, err := models.Mongo.Collection("actions").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
	if err != nil {
		return err, []int64{}
	}
	// display the results
	var results []bson.M
	if err = cursor.All(context.TODO(), &results); err != nil {
		if err != nil {
			return err, []int64{}
		}
	}
	for _, result := range results {
		Type := result["_id"].(int64)
		res[Type] = int64(result["count"].(int32))
	}
	return nil, res
}

func (service *GetGraphService) getBrowser() (error, []int64) {
	//初始化结果数组
	res := make([]int64, 7)
	startTime := service.EndTime - 30*24*60*60*1000
	// create the stage
	groupStage := bson.D{
		{"$group", bson.D{
			{"_id", "$browser"},
			{"count", bson.D{
				{"$sum", 1},
			}},
		},
		}}
	match := bson.D{
		{"$match", bson.D{{"webid", service.WebID},
			{"time", bson.D{{"$gt", startTime}}},
			{"time", bson.D{{"$lte", service.EndTime}}},
		}},
	}
	// pass the stage into a pipeline
	// pass the pipeline as the second paramter in the Aggregate() method
	cursor, err := models.Mongo.Collection("actions").Aggregate(context.TODO(), mongo.Pipeline{match, groupStage})
	if err != nil {
		return err, []int64{}
	}
	// display the results
	var results []bson.M
	if err = cursor.All(context.TODO(), &results); err != nil {
		if err != nil {
			return err, []int64{}
		}
	}
	for _, result := range results {
		Type := result["_id"].(int64)
		res[Type] = int64(result["count"].(int32))
	}
	return nil, res
}
